#include "serveroperation.h"
#include "jsonparse.h"
#include "requestfactory.h"
#include "respondfactory.h"
#include <QDebug>
#include <QThreadPool>
#include <QCryptographicHash>
#include "aescrypto.h"

AcceptConnect::AcceptConnect()
{
    // 当前对象在线程池中执行完毕, 自动析构
    setAutoDelete(true);
    // 实例化一个用于监听的服务器对象
    m_server = new TcpServer;
    // 设置监听
    // 这个端口来自于服务器端的配置文件
    SerConfig conf = JsonParse::getInstance()->readServerConfig();
    emit showMsg("给服务器设置监听...");
    m_server->setListen(conf.port.toInt());

    // 创建共享内存对象
    m_shm = new SeckeyShm(conf.shmKey, conf.shmMaxNode.toInt());
}

AcceptConnect::~AcceptConnect()
{
    delete m_server;
    delete m_shm;
}

void AcceptConnect::run()
{
    // 循环检测客户端的连接
    while(1)
    {
        if(m_stop)
        {
            break;
        }
        // 阻塞等待客户端的连接
        showMsg("阻塞等待客户端连接...");
        TcpSocket* tcp = m_server->acceptConn(5);
        if(tcp == NULL)
        {
            // 创建通信对象失败, 重试
            showMsg("和客户端建立连接超时, 重试...");
            continue;
        }
        // 创建一个新的用于和客户端通信的对象, 这个对象也要被添加到线程池中
        // 在客户端发起的每个请求都在服务器端对应一个任务对象, 这些对象都会被放到线程池中被处理掉
        // 这个对象对应的任务执行完毕, 对象就自动销毁了
        Communication* comm = new Communication(tcp, m_shm);
        // 添加通信对象到线程池中
        QThreadPool::globalInstance()->start(comm);
        // 信号连接信号, 实现数据的中转
        connect(comm, &Communication::showMsg, this, &AcceptConnect::showMsg);
    }
}

void AcceptConnect::stop()
{
    m_stop = true;
}

Communication::Communication(TcpSocket* tcp, SeckeyShm* shm)
{
    // 当前对象在线程池中执行完毕, 自动析构
    setAutoDelete(true);
    m_tcp = tcp;
    // 创建共享内存
    SerConfig conf = JsonParse::getInstance()->readServerConfig();
    // 当这个对象被销毁, 这块共享内存也就不存在了
    // 解决方案: 可以在一个不被析构的对象中创建共享内存, 然后传递到当前对象中
    // m_shm = new SeckeyShm(conf.shmKey, conf.shmMaxNode.toInt());
    m_shm = shm;    // 保存共享内存对象的地址
    // 创建数据库操作实例对象
    m_db = new DBOperation;
    // 连接数据库服务器 -> 192.168.237.128
    bool bl = m_db->connectDB(conf.dbUserName, conf.dbPasswd,
                              conf.dbHost, conf.dbPort.toInt(), conf.dbName);
    qDebug() << "数据库连接状态: " << bl;
}

Communication::~Communication()
{
}

// 秘钥协商
void Communication::seckeyConsult(RequestInfo* info)
{
    /*
     * 通过 run() 函数得到了客户端的原始数据, 该函数中应该基于客户端
     * 发送过来的原始数据进行处理:
     * ====================数据校验====================
     *  1. 校验客户端的身份是否真实: clientID, serverID
     *  2. 校验客户端的请求数据有没有被篡改: 数字签名的校验
     * ====================业务流程====================
     *  1. 生成一个对称加密的秘钥 -> 基于AES (16, 24, 32字节)
     *  2. 生成的秘钥如果要发送给客户端, 需要加密 -> 公钥
     *  3. 序列化 -> 得到字符串
     *  4. 通信 -> 将序列化的数据发送给客户端
    */
    emit showMsg("开始秘钥协商, 在这初始化数据...");
    // 数据初始化
    // 创建RSA对象(空对象)
    RsaCrypto rsa;
    // rsa对象中有了一个可用的公钥
    rsa.parseStringToKey(info->data);
    // 通过客户端传递过来的公钥初始化这个对象

    // 1. 身份和数据校验
    RespondInfo resInfo;
    bool bl = identifyVerify(rsa, info);
    if(!bl)
    {
        // 校验失败, 直接将错误信息回复给客户端即可, 不继续业务流程的处理
        resInfo.status = false;
        emit showMsg("客户端身份或者客户端签名校验失败, 秘钥协商失败, 拜拜!!!");
    }
    else
    {
        // 2. 业务流程的处理
        // 2.1 生成一个对称加密的秘钥 -> 基于AES (16, 24, 32字节)
        QString key = generateAesKey(L16);
        emit showMsg("生成了对称加密的秘钥: " + key);

        // 秘钥的ID是通过数据库生成的
        int keyid = m_db->getKeyID();
        SeckeyInfo keyinfo(true, keyid, info->clientID, info->serverID, key);

        // 2.3 秘钥写入到数据库中
        bool flag = m_db->insertSeckeyInfo(keyinfo);
        if(flag)
        {
            // 2.4 秘钥写入到共享内存中
            m_shm->writeShm(keyinfo);
            emit showMsg("将秘钥写入了共享内存 和数据库中...");
            emit showMsg("****** 恭喜, 秘钥协商成功了 ******");
            resInfo.status = true;
            // 初始化要回复的数据的格式: {"KeyID":"1111", "AesKey":"xxx"}
            QStringList keys, values;
            keys << "KeyID" << "AesKey";
            // 2.2. 生成的秘钥如果要发送给客户端, 需要加密 -> 公钥
            values << QString::number(keyid) << rsa.rsaPubKeyEncrypt(key);
            resInfo.data = JsonParse::getInstance()->generateSimpleObj(keys, values);

            // 生成外联接口需要的配置文件
            // {"ServerID":"xx", "ClientID", "xx", "ShmKey":"xx"}
            keys.clear();
            values.clear();
            keys <<"ShmKey" << "ServerID" << "ClientID";
            SerConfig serconf = JsonParse::getInstance()->readServerConfig();
            values << serconf.shmKey << serconf.serverID << "";
            JsonParse::getInstance()->writeInterfaceJson(keys, values);
        }
        else
        {
            resInfo.status = false;
            emit showMsg("****** Sorry, 秘钥协商失败了 ******");
        }
    }

    // 序列化 -> 得到字符串
    sendSerializeMsg(&resInfo, "回复数据给客户端, 秘钥协商结束了...");
}

void Communication::seckeyVerify(RequestInfo *info)
{
    showMsg("开始进行秘钥校验, 开始解析数据...");
    // 数据解析
    // info->data 中的数据格式: {"Hash":"xxx", "PubKey":"xxxxyyyzz"}
    JsonParse* json = JsonParse::getInstance();
    QString hash224 = json->simpleObjValue(info->data, "Hash");
    QString pubkey = json->simpleObjValue(info->data, "PubKey");

    // 数据初始化
    // 创建RSA对象(空对象)
    RsaCrypto rsa;
    // rsa对象中有了一个可用的公钥
    rsa.parseStringToKey(pubkey);
    // 通过客户端传递过来的公钥初始化这个对象

    // 1. 身份和数据校验
    RespondInfo resInfo;
    bool bl = identifyVerify(rsa, info);
    if(!bl)
    {
        // 校验失败, 直接将错误信息回复给客户端即可, 不继续业务流程的处理
        resInfo.status = false;
        showMsg("客户端身份或者客户端签名校验失败, 秘钥校验失败, 拜拜!!!");
    }
    else
    {
        // 2. 业务流程的处理
        // 2.1 读共享内存 -> 当前服务器和对应的客户端协商的秘钥读出
        SeckeyInfo keyinfo = m_shm->readShm(info->clientID, info->serverID);
        // 2.2 计算哈希
        QString sha224 = QCryptographicHash::hash(keyinfo.seckey, QCryptographicHash::Sha224);
        // 2.3 哈希的比较(自己生成的和客户端传过来的)
        if(sha224 == hash224)
        {
            resInfo.status = true;
            showMsg("*********秘钥校验成功, 恭喜*********");
        }
        else
        {
            resInfo.status = false;
            showMsg("*********秘钥校验失败, 呜呜呜*********");
        }
    }
    sendSerializeMsg(&resInfo, "回复数据给客户端, 秘钥校验结束了...");
}

void Communication::seckeyLogout(RequestInfo *info)
{
    emit showMsg("客户端请求进行秘钥状态修改...");
    // 解析json字符串得到 公钥
    // {"KeyID":"123", "Status":"1", "PubKey":"xxxyyz"}
    JsonParse* json = JsonParse::getInstance();
    int keyID = json->simpleObjValue(info->data, "KeyID").toInt();
    QString status = json->simpleObjValue(info->data, "Status");
    QString pubKey = json->simpleObjValue(info->data, "PubKey");

    RsaCrypto rsa;
    // 通过pem格式公钥字符串初始化 rsa 对象
    emit showMsg("开始校验客户端身份和数字签名...");
    rsa.parseStringToKey(pubKey);
    // 客户端身份和数据校验
    RespondInfo resInfo;
    bool bl = identifyVerify(rsa, info);
    if(!bl)
    {
        resInfo.status = false;
        emit showMsg("客户端身份或数字签名校验失败!!!");
    }
    else
    {
        emit showMsg("客户端身份或数字签名校验成功!!!");
        // 修改数据库中秘钥的状态
        bl = m_db->modifyKeyStatus(status.toInt(), keyID);
        if(bl)
        {
            // 修改共享内存
            SeckeyInfo keyinfo = m_shm->readShm(keyID);
            keyinfo.status = status.toInt();
            m_shm->writeShm(keyinfo);
            resInfo.status = true;
            emit showMsg(" ****** 秘钥注销操作成功, 拜拜 ******");
        }
        else
        {
            resInfo.status = false;
            emit showMsg(" ****** 秘钥注销操作失败, 呜呜呜 ******");
        }
    }
    sendSerializeMsg(&resInfo, "回复数据给客户端, 秘钥注销结束了...");
}

void Communication::seckeyLogin(RequestInfo *info)
{
    emit showMsg("客户端请求进行登录...");
    // AES解密
    SeckeyInfo keyinfo = m_shm->readShm(info->clientID, info->serverID);
    AesCrypto aes(keyinfo.seckey);
    QString decStr = aes.aesCBCDecrypt(info->data);
    // 解析json字符串得到 公钥
    // {"PubKey":"xxxxyyyzz", "NetID":"xxx", "Password":"1"}
    JsonParse* json = JsonParse::getInstance();
    QString netid = json->simpleObjValue(decStr, "NetID");
    QString passwd = json->simpleObjValue(decStr, "Password");
    QString pubKey = json->simpleObjValue(decStr, "PubKey");

    RsaCrypto rsa;
    // 通过pem格式公钥字符串初始化 rsa 对象
    emit showMsg("开始校验客户端身份和数字签名...");
    rsa.parseStringToKey(pubKey);
    // 客户端身份和数据校验
    RespondInfo resInfo;
    bool bl = identifyVerify(rsa, info);
    if(!bl)
    {
        resInfo.status = false;
        emit showMsg("客户端身份或数字签名校验失败!!!");
    }
    else
    {
        emit showMsg("客户端身份或数字签名校验成功!!!");
        // 查询数据库, 查询客户端发送的id和密码是否和数据库中相同
        bl = m_db->userAuthentication(netid, passwd);
        resInfo.status = bl;
    }
    sendSerializeMsg(&resInfo, "回复数据给客户端, 登录校验结束了...");
}

void Communication::run()
{
    // 通信的流程
    // 1. 接收客户端发送的请求数据 -> 阻塞的动作, 有数据之后解除阻塞
    showMsg("接收客户端数据, 并且进行反序列化处理...");
    QString recvStr = m_tcp->recvMsg();

    // 2. 对请求数据进行反序列化
    // 创建工厂类对象
    CodecFactory* f = new RequestFactory(recvStr);
    // 创建编解码对象
    Codec* c = f->createCodec();
    RequestInfo reqInfo;
    c->decodeMsg(&reqInfo); // 原始数据被写入到reqInfo对象中
    delete f;
    delete c;

    // 3. 通过 cmdtype 判断客户端要让服务器干什么
    switch(reqInfo.cmdtype)
    {
    case CmdType::Consult:
        // 秘钥协商
        seckeyConsult(&reqInfo);
        break;
    case CmdType::Verify:
        seckeyVerify(&reqInfo);
        break;
    case CmdType::Logout:
        seckeyLogout(&reqInfo);
        break;
    case CmdType::Login:
        seckeyLogin(&reqInfo);
    default:
        break;
    }
    // 4. 处理的数据初始 -> 序列化 -> 回复数据
}

bool Communication::identifyVerify(RsaCrypto& rsa, RequestInfo* info)
{
    // 1. 校验客户端身份(查询数据库)
    bool flag = m_db->isValideClient(info->clientID);
    // 2. 校验数字签名(私钥加密之后的数据, 需要公钥解密)
    // 需要使用RSA的类
    /*
     * 签名操作
     QByteArray sha1 = QCryptographicHash::hash(
                info.data.toUtf8(), QCryptographicHash::Sha1);
     info.sign = rsa.rsaSign(sha1);   // 对data进行签名
    */
    QByteArray sha1 = QCryptographicHash::hash(
               info->data.toUtf8(), QCryptographicHash::Sha1);
    bool bl = rsa.rsaVerify(sha1, info->sign);

    SerConfig conf = JsonParse::getInstance()->readServerConfig();
    if(bl && info->serverID==conf.serverID && flag)
    {
        return true;
    }
    return false;
}

// 秘钥字符串的格式: a-z, A-Z, 0-9, 特殊字符(!~@#$%^&*()_+=-)
QString Communication::generateAesKey(Communication::KeyLen len)
{
    // 设置随机数种子, 一般是一时间为种
    qsrand(time(NULL));
    char cc;
    QByteArray ret;
    QByteArray tmp("!~@#$%^&*()_+=-");
    ret.clear();

    // 循环 len 次生成定长的字符串
    for(int i=0; i<len; ++i)
    {
        // 循环一次(随机)生成一种字符
        // Qt中 qrand() == c/c++ rand()
        int flag = qrand()%4;
        switch(flag)
        {
        case 0:     // a-z (26个)
            cc = qrand()%26 + 'a';
            break;
        case 1:     // A-Z
            cc = qrand()%26 + 'A';
            break;
        case 2:     // 0-9
            cc = qrand()%10 + '0';
            break;
        case 3:     // 特殊字符(!~@#$%^&*()_+=-)
            cc = tmp.at(qrand()%tmp.size());
            break;
        default:
            break;
        }
        // 将生成的字符添加到对象尾部
        ret.append(cc);
    }
    return QString(ret);
}

void Communication::sendSerializeMsg(RespondInfo *info, QString log)
{
    // 序列化 -> 得到字符串
    CodecFactory* f = new RespondFactory(info);
    Codec* c = f->createCodec();
    QString msg = c->encodeMsg();
    // 通信 -> 将序列化的数据发送给客户端
    m_tcp->sendMsg(msg);
    m_tcp->disConnect();
    delete m_tcp;
    delete f;
    delete c;
    emit showMsg(log);
    emit showMsg(" ================== The End ==================");
}
